﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Laharsub.Server.Wcf
{
    class ScalarAsyncResult<T> : IAsyncResult
    {
        static AsyncCallback commandCallback = new AsyncCallback(OnCommandCompleted);

        AsyncCallback callback;
        object state;
        Exception exception;
        bool completedSynchronously;
        bool completed;
        ManualResetEvent waitHandle = new ManualResetEvent(false);
        T result;
        Func<IAsyncResult, T> end;

        public ScalarAsyncResult(Func<AsyncCallback, object, IAsyncResult> begin, Func<IAsyncResult, T> end, AsyncCallback callback, object state)
        {
            this.callback = callback;
            this.state = state;
            this.end = end;

            IAsyncResult r = begin(commandCallback, this);
            if (r.CompletedSynchronously)
            {
                this.ProcessResult(r, true);
            }
        }

        public static T End(IAsyncResult result)
        {
            ScalarAsyncResult<T> r = result as ScalarAsyncResult<T>;
            if (r == null)
            {
                throw new InvalidOperationException("Invalid async result.");
            }
            r.waitHandle.WaitOne();
            if (r.exception != null)
            {
                throw r.exception;
            }
            else
            {
                return r.result;
            }
        }

        static void OnCommandCompleted(IAsyncResult result)
        {
            if (!result.CompletedSynchronously)
            {
                ((ScalarAsyncResult<T>)result.AsyncState).ProcessResult(result, false);
            }
        }

        void ProcessResult(IAsyncResult result, bool synchronous)
        {
            try
            {
                this.result = this.end(result);
            }
            catch (Exception e)
            {
                this.Complete(synchronous, e);
            }
            this.Complete(synchronous, null);
        }

        protected void Complete(bool completedSynchronously, Exception e)
        {
            this.exception = e;
            this.completedSynchronously = completedSynchronously;
            this.completed = true;
            this.waitHandle.Set();
            if (this.callback != null)
            {
                // TODO add support for callback exceptions
                this.callback(this);
            }
        }

        public object AsyncState
        {
            get { return this.state; }
        }

        public WaitHandle AsyncWaitHandle
        {
            get { return this.waitHandle; }
        }

        public bool CompletedSynchronously
        {
            get { return this.completedSynchronously; }
        }

        public bool IsCompleted
        {
            get { return this.completed; }
        }
    }
}
